# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd

from src.sharedSrc import extract_value_and_unit, is_valid_hex


def binChartData(df: pd) -> dict:
    """
    get bin from csv
    :param df: pandas read csv, type Dataframe
    :return: process bin data, type dict
    """
    measure_list = [col for col in df.columns if 'Measure' in col]

    x_coord_df = df[df['Force'] == 'XCoord']
    y_coord_df = df[df['Force'] == 'YCoord']
    bin_df = df[df['Force'] == 'Bin']
    data = {}

    if len(measure_list):
        value = bin_df[measure_list[0]].astype(int).iloc[0]
        x_max = value
        x_min = value
        y_max = value
        y_min = value
    else:
        return data
    for measure in measure_list:
        x_values = x_coord_df[measure].astype(int).values[0]
        y_values = y_coord_df[measure].astype(int).values[0]
        bin_values = bin_df[measure].iloc[0]  # Assuming Bin values are consistent

        if x_min > x_values:
            x_min = x_values
        elif x_max < x_values:
            x_max = x_values
        if y_min > y_values:
            y_min = y_values
        elif y_max < y_values:
            y_max = y_values
        if bin_values in data:
            data[bin_values]['x'].append(x_values)
            data[bin_values]['y'].append(y_values)
            data[bin_values]['Bin'].update({f"{x_values} {y_values}": bin_values})
        else:
            data[bin_values] = {
                'x': [x_values],
                'y': [y_values],
                'Bin': {f"{x_values} {y_values}": bin_values}
            }

    sample_wafer_data = {
        "Data": data,
        "Info": (1.0, "200um", False, False),
        "Bounds": (x_max, x_min, y_max, y_min)
    }

    return sample_wafer_data


def cateChartData(df: pd) -> dict:
    """
     get bin from csv
     :param df: pandas read csv, type Dataframe
     :return: process bin data, type dict
     """

    measure_list = [col for col in df.columns if 'Measure' in col]
    x_coord_df = df[df['Force'] == 'XCoord']
    y_coord_df = df[df['Force'] == 'YCoord']
    bin_df = df[df['Force'] == 'Cate']
    data = {}

    if len(measure_list):
        value = bin_df[measure_list[0]].astype(int).iloc[0]
        x_max = value
        x_min = value
        y_max = value
        y_min = value
    else:
        return data
    for measure in measure_list:
        x_values = x_coord_df[measure].astype(int).values[0]
        y_values = y_coord_df[measure].astype(int).values[0]
        bin_values = bin_df[measure].iloc[0]  # Assuming Bin values are consistent

        if x_min > x_values:
            x_min = x_values
        elif x_max < x_values:
            x_max = x_values
        if y_min > y_values:
            y_min = y_values
        elif y_max < y_values:
            y_max = y_values
        if bin_values in data.keys():
            data[bin_values]['x'].append(x_values)
            data[bin_values]['y'].append(y_values)
            data[bin_values]['Bin'].update({f"{x_values} {y_values}": bin_values})
        else:
            data[bin_values] = {
                'x': [x_values],
                'y': [y_values],
                'Bin': {f"{x_values} {y_values}": bin_values}
            }

    sample_wafer_data = {
        "Data": data,
        "Info": (1.0, "200um", False, False),
        "Bounds": (x_max, x_min, y_max, y_min)
    }

    return sample_wafer_data


# def mapChartData(df: pd, testName: str, signal: str, lower_ma: float = 0.0, high_ma: float = 3.0,
#                  lower_mv: float = -500.0, high_mv: float = 500.0, precision: int = 3) -> dict:
#     """
#     get bin from csv
#     :param precision:
#     :param high_mv:
#     :param lower_mv:
#     :param testName:
#     :param signal:
#     :param lower_ma:
#     :param high_ma:
#     :param df: pandas read csv, type Dataframe
#     :return: process bin data, type dict
#     """
#
#     # Filter DataFrame once instead of multiple times
#     data = df[(df['TestName'] == testName) & (df['Signal'] == signal)]
#     data = data.filter(regex='^Measure')
#     if data.empty:
#         return {}
#     units = ["mV", "mA", "uA", "nA", "A", "uV", "nV", "V"]
#     unit_conversion = [1, 1, 10e-3, 10e-6, 1e3, 10e-3, 10e-6, 1e3]
#     set_color = 'value'
#     x_coord, y_coord = [], []
#     x_coord_df, y_coord_df = df[df['Force'] == 'XCoord'], df[df['Force'] == 'YCoord']
#     xy_measure, lowerMeasure, highMeasure, midMeasure = {}, {}, {}, {}
#     x_low, y_low, x_mid, y_mid, x_high, y_high = [], [], [], [], [], []
#     unit = None
#     for v in data:
#         value = data[v].values[0]
#         if pd.isna(value) or not value:
#             continue
#
#         x, y = x_coord_df[v].values, y_coord_df[v].values
#         key = f"{x[0]} {y[0]}"
#         for u, conversion in zip(units, unit_conversion):
#             if u in value:
#                 parsed_value = round(float(value.replace(u, '')) * conversion, precision)
#                 unit = 'mA' if "A" in u else 'mV'
#
#                 if unit == 'mV':
#                     if parsed_value > high_mv:
#                         x_high.extend(x)
#                         y_high.extend(y)
#                         highMeasure[key] = str(parsed_value)
#                     elif parsed_value < lower_mv:
#                         x_low.extend(x)
#                         y_low.extend(y)
#                         lowerMeasure[key] = str(parsed_value)
#                     else:
#                         x_mid.extend(x)
#                         y_mid.extend(y)
#                         midMeasure[key] = str(parsed_value)
#                 elif unit == 'mA':
#                     if parsed_value > high_ma:
#                         x_high.extend(x)
#                         y_high.extend(y)
#                         highMeasure[key] = str(parsed_value)
#                     elif parsed_value < lower_ma:
#                         x_low.extend(x)
#                         y_low.extend(y)
#                         lowerMeasure[key] = str(parsed_value)
#                     else:
#                         x_mid.extend(x)
#                         y_mid.extend(y)
#                         midMeasure[key] = str(parsed_value)
#                 break
#         else:
#             # 非电压 电流组
#             x_coord.extend(x)
#             y_coord.extend(y)
#             xy_measure[key] = value
#             set_color = value
#     if len(xy_measure):
#         x_coord, y_coord = list(map(int, x_coord)), list(map(int, y_coord))
#         x_min, x_max, y_min, y_max = min(x_coord), max(x_coord), min(y_coord), max(y_coord)
#     elif len(lowerMeasure) or len(midMeasure) or len(highMeasure):
#         # 转换坐标
#         x_low, y_low = list(map(int, x_low)), list(map(int, y_low))
#         x_mid, y_mid = list(map(int, x_mid)), list(map(int, y_mid))
#         x_high, y_high = list(map(int, x_high)), list(map(int, y_high))
#         # 将所有x坐标和y坐标整合到两个列表中
#         all_x_coords = x_low + x_mid + x_high
#         all_y_coords = y_low + y_mid + y_high
#         # 查找x列表和y列表中的最大值与最小值
#         x_min, x_max, y_min, y_max = min(all_x_coords), max(all_x_coords), min(all_y_coords), max(all_y_coords)
#     else:
#         return {}
#
#     sample_wafer_data = {
#         "Data": {
#
#         },
#         "Info": 1.0,
#         "Unit": unit,
#         "Bounds": (x_max, x_min, y_max, y_min)
#     }
#     if len(xy_measure):
#         sample_wafer_data["Data"][set_color] = {"x": x_coord, "y": y_coord, "measure": xy_measure,
#                                                 'name': f'{testName} {signal}'}
#     else:
#         if lowerMeasure:
#             sample_wafer_data["Data"]['lower'] = {"x": x_low, "y": y_low, "measure": lowerMeasure,
#                                                   'name': f'{testName} {signal} lower'}
#         if midMeasure:
#             sample_wafer_data["Data"]['middle'] = {"x": x_mid, "y": y_mid, "measure": midMeasure,
#                                                    'name': f'{testName} {signal} middle'}
#         if highMeasure:
#             sample_wafer_data["Data"]['higher'] = {"x": x_high, "y": y_high, "measure": highMeasure,
#                                                    'name': f'{testName} {signal} higher'}
#     return sample_wafer_data
# def mapSplitData2(df: pd, testName: str, signal: str, precision: dict, split: int = 5) -> dict:
#     """
#     get bin from csv
#     :param split:
#     :param precision:
#     :param testName:
#     :param signal:
#     :param df: pandas read csv, type Dataframe
#     :return: process bin data, type dict
#     """
#
#     # Filter DataFrame once instead of multiple times
#     data = df[(df['TestName'] == testName) & (df['Signal'] == signal)]
#     data = data.filter(regex='^Measure')
#     if data.empty:
#         return {}
#     units = ["mV", "mA", "uA", "nA", "A", "uV", "nV", "V"]
#     unit_conversion = [1, 1, 10e-3, 10e-6, 1e3, 10e-3, 10e-6, 1e3]
#     set_color = 'value'
#     x_coord, y_coord = [], []
#     x_coord_df, y_coord_df = df[df['Force'] == 'XCoord'], df[df['Force'] == 'YCoord']
#     xy_measure, mvaMeasure = {}, {}
#     x_mva, y_mva = [], []
#     unit = None
#     for v in data:
#         value = data[v].values[0]
#         if pd.isna(value) or not value:
#             continue
#
#         x, y = x_coord_df[v].values, y_coord_df[v].values
#         key = f"{x[0]} {y[0]}"
#         for u, conversion in zip(units, unit_conversion):
#             if u in value:
#                 parsed_value = float(value.replace(u, '')) * conversion
#                 unit = 'mA' if "A" in u else 'mV'
#                 x_mva.extend(x)
#                 y_mva.extend(y)
#                 mvaMeasure[key] = parsed_value
#                 break
#         else:
#             # 非电压 电流组
#             x_coord.extend(x)
#             y_coord.extend(y)
#             xy_measure[key] = str(value)
#             set_color = value
#     if xy_measure:
#         x_coord, y_coord = list(map(int, x_coord)), list(map(int, y_coord))
#         x_min, x_max, y_min, y_max = min(x_coord), max(x_coord), min(y_coord), max(y_coord)
#     elif mvaMeasure:
#         # 转换坐标
#         x_mva, y_mva = list(map(int, x_mva)), list(map(int, y_mva))
#         # 查找x列表和y列表中的最大值与最小值
#         x_min, x_max, y_min, y_max = min(x_mva), max(x_mva), min(y_mva), max(y_mva)
#     else:
#         return {}
#
#     sample_wafer_data = {
#         "Data": {
#
#         },
#         "Info": 1.0,
#         "Unit": unit,
#         "Bounds": (x_max, x_min, y_max, y_min),
#         "TestName": testName,
#         "Signal": signal
#     }
#     if xy_measure:
#         sample_wafer_data["Data"][set_color] = {"x": x_coord, "y": y_coord, "measure": xy_measure,
#                                                 'name': f'{testName} {signal}'}
#     else:
#         precision = precision.get(unit, 3)
#         list_measure = np.array(list(mvaMeasure.values()))
#         min_value, max_value = list_measure.min(), list_measure.max()
#         split_edges = np.linspace(min_value, max_value, split + 1)
#
#         for i in range(split):
#             xc, yc, measure = [], [], {}
#             set_color = f'split_{i}'
#             lower_bound = split_edges[i]
#             upper_bound = split_edges[i + 1]
#
#             # Assign each value to the appropriate bin
#             for key, value in mvaMeasure.items():
#                 x, y = map(int, key.split())
#                 if lower_bound <= value < upper_bound:
#                     xc.append(x)
#                     yc.append(y)
#                     measure[key] = str(round(value, precision))
#                 elif i == split - 1 and value == upper_bound:
#                     # Ensure the maximum value is included in the last bin
#                     xc.append(x)
#                     yc.append(y)
#                     measure[key] = str(round(value, precision))
#
#             if measure:  # Only add non-empty splits
#                 sample_wafer_data["Data"][set_color] = {
#                     "x": xc,
#                     "y": yc,
#                     "measure": measure,
#                     'name': f'{set_color}: [{lower_bound:.{precision}f}~{upper_bound:.{precision}f}]'
#                 }
#     return sample_wafer_data


def mapSplitData(df: pd, testName: str, signal: str, precision: dict, split: int = 5, target_ma=None,
                 target_mv=None) -> dict:
    """
    get bin from csv
    :param target_mv:
    :param target_ma:
    :param split:
    :param precision:
    :param testName:
    :param signal:
    :param df: pandas read csv, type Dataframe
    :return: process bin data, type dict
    """
    if split == 0:
        split = 1
    # Filter DataFrame once instead of multiple times
    data = df[(df['TestName'] == testName) & (df['Signal'] == signal)]
    data = data.filter(regex='^Measure')
    if data.empty:
        return {}
    set_color = 'value'
    x_coord, y_coord = [], []
    x_coord_df, y_coord_df = df[df['Force'] == 'XCoord'], df[df['Force'] == 'YCoord']
    xy_measure, mvaMeasure = {}, {}
    x_mva, y_mva = [], []
    unit = None
    for v in data:
        value = data[v].values[0]
        if pd.isna(value) or not value:
            continue
        value_and_unit = extract_value_and_unit(value)
        x, y = x_coord_df[v].values, y_coord_df[v].values
        key = f"{x[0]} {y[0]}"
        if value_and_unit and target_ma and target_mv:
            u = value_and_unit['unit']
            v = value_and_unit['value']
            if u == '%' or u == '%':
                unit = '%'
                mvaMeasure[key] = v
            else:
                ifv = 1 if 'V' in u else 0
                if ifv:
                    parsed_value = convert_unit(v, u, target_mv)
                    parsed_value = round(parsed_value, precision.get(u, 3))
                    unit = target_mv
                else:
                    parsed_value = convert_unit(v, u, target_ma)
                    parsed_value = round(parsed_value, precision.get(u, 3))
                    unit = target_ma
                mvaMeasure[key] = parsed_value
            x_mva.extend(x)
            y_mva.extend(y)
        else:
            # 非电压 电流组
            x_coord.extend(x)
            y_coord.extend(y)
            xy_measure[key] = value

    if xy_measure:
        x_coord, y_coord = list(map(int, x_coord)), list(map(int, y_coord))
        x_min, x_max, y_min, y_max = min(x_coord), max(x_coord), min(y_coord), max(y_coord)
    elif mvaMeasure:
        # 转换坐标
        x_mva, y_mva = list(map(int, x_mva)), list(map(int, y_mva))
        # 查找x列表和y列表中的最大值与最小值
        x_min, x_max, y_min, y_max = min(x_mva), max(x_mva), min(y_mva), max(y_mva)
    else:
        return {}

    sample_wafer_data = {
        "Data": {

        },
        "Info": 1.0,
        "Unit": unit,
        "Bounds": (x_max, x_min, y_max, y_min),
        "TestName": testName,
        "Signal": signal
    }
    if xy_measure:
        if (target_mv is None and target_ma is None) or is_valid_hex(xy_measure[f'{x_coord[0]} {y_coord[0]}']) is False:
            sample_wafer_data["Data"][set_color] = {"x": x_coord, "y": y_coord, "measure": xy_measure,
                                                    'name': f'{testName} {signal}'}
            return sample_wafer_data

        data = sample_wafer_data["Data"]
        for x, y in zip(x_coord, y_coord):
            key = f"{x} {y}"
            val = xy_measure[key]
            if val not in data:
                data[val] = {"x": [], "y": [], "measure": {}, 'name': val}

            data[val]['x'].append(x)
            data[val]['y'].append(y)
            data[val]["measure"][key] = val
        # 占比计算
        for key in data:
            len_measure = len(data[key]['measure'])
            data[key]['name'] = f'{key} N: {len_measure} P: {len_measure / len(xy_measure) * 100:.2f}%'
    else:
        pre_unit = 'mA' if 'A' in unit else 'mV'
        precision = precision.get(pre_unit, 3)
        list_measure = np.array(list(mvaMeasure.values()))
        min_value, max_value = list_measure.min(), list_measure.max()
        split_edges = np.linspace(min_value, max_value, split + 1)

        for i in range(split):
            xc, yc, measure = [], [], {}
            set_color = f'split_{i}'
            lower_bound = split_edges[i]
            upper_bound = split_edges[i + 1]

            # Assign each value to the appropriate bin
            for key, value in mvaMeasure.items():
                x, y = map(int, key.split())
                if lower_bound <= value < upper_bound:
                    xc.append(x)
                    yc.append(y)
                    measure[key] = str(round(value, precision))
                elif i == split - 1 and value == upper_bound:
                    # Ensure the maximum value is included in the last bin
                    xc.append(x)
                    yc.append(y)
                    measure[key] = str(round(value, precision))

            if measure:  # Only add non-empty splits
                sample_wafer_data["Data"][set_color] = {
                    "x": xc,
                    "y": yc,
                    "measure": measure,
                    'name': f'[{lower_bound:.{precision}f}~{upper_bound:.{precision}f}] N: {len(measure)} P: {len(measure) / len(mvaMeasure) * 100:.2f}%'
                }

    return sample_wafer_data


def trend_data(df, testName: str, signal: str) -> dict:
    trendData = {
        "TestInfo": {1: {}},
        "Data": {1: {}}
    }

    # Filter DataFrame once instead of multiple times
    data = df[(df['TestName'] == testName) & (df['Signal'] == signal)]

    # Initialize relevant lists and conversion factors
    measure_list = ['LowLimit', 'HighLimit']
    units = ["mV", "mA", "uA", "nA", "A", "uV", "nV", "V"]
    unit_conversion = [1, 1, 10e-3, 10e-6, 1e3, 10e-3, 10e-6, 1e3]

    voltages = [data[measure].values[0] for measure in measure_list]
    parsed_voltages = []
    unit = None

    for v in voltages:
        for u in units:
            if u in v:
                parsed_value = float(v.replace(u, ''))
                parsed_voltages.append(parsed_value * unit_conversion[units.index(u)])
                unit = u
                break
        if unit is None:
            return trendData

    measure_columns = [col for col in df.columns if 'Measure' in col]

    XCoordDF = df[df['Force'] == 'XCoord']
    YCoordDF = df[df['Force'] == 'YCoord']

    dataList = []
    XCoord = []
    YCoord = []

    for measure in measure_columns:
        xyData = data[measure].values[0]
        if pd.isna(xyData):
            continue

        x_values = int(XCoordDF[measure].values[0])
        y_values = int(YCoordDF[measure].values[0])

        for u in units:
            if u in xyData:
                rmUnitData = float(xyData.replace(u, '')) * unit_conversion[units.index(u)]
                dataList.append(rmUnitData)
                XCoord.append(x_values)
                YCoord.append(y_values)
                break

    if len(dataList) == 0:
        return trendData
    mean = np.mean(dataList)
    median = np.median(dataList)
    lSpec = np.min(dataList)
    hSpec = np.max(dataList)

    xData = {
        "dutList": XCoord,
        "dataList": dataList,
        "Mean": mean,
        "Median": median,
        "TestName": testName,
        "Signal": signal
    }
    yData = {
        "dutList": YCoord,
        "dataList": dataList,
        "Mean": mean,
        "Median": median,
        "TestName": testName,
        "Signal": signal
    }

    trendData["TestInfo"][1] = {
        "LLimit": parsed_voltages[0],
        "HLimit": parsed_voltages[1],
        "Unit": "mV/mA",
        "LSpec": lSpec,
        "HSpec": hSpec
    }
    trendData["Data"][1][0] = xData
    trendData["Data"][1][1] = yData

    return trendData


def bin_summary_data(df: pd) -> dict:
    """设置bin 总结数据"""
    cate = df[(df['Force'] == 'Cate')].fillna("")
    bin = df[(df['Force'] == 'Bin')].fillna("")
    hBin = dict()
    sBIN = dict()
    HBIN_Ticks = dict()
    SBIN_Ticks = dict()
    cnt1 = 0
    cnt2 = 0
    for i, j in zip(bin, cate):
        b = bin[i].values[0]
        c = cate[j].values[0]
        if b != "" and b != "Bin":
            if b in hBin:
                hBin[b] += 1
            else:
                hBin[b] = 1
                HBIN_Ticks[b] = (cnt1, b)
                cnt1 += 1
        if c != "" and c != "Cate":
            if c in sBIN:
                sBIN[c] += 1
            else:
                sBIN[c] = 1
                SBIN_Ticks[c] = (cnt2, c)
                cnt2 += 1

    bin_data = {
        "HBIN": {1: hBin},
        "SBIN": {1: sBIN},  # Use similar data structure if SBIN is needed
        "HBIN_Ticks": HBIN_Ticks,
        "SBIN_Ticks": SBIN_Ticks  # Use similar data structure if SBIN_Ticks is needed
    }

    return bin_data


# def wafer_data_summary(sample_wafer_data: dict) -> dict:
#     ND = {}
#     ND_Ticks = {}
#     RD = {}
#     RD_Ticks = {}
#     min_value = 0
#     max_value = 0
#     data = sample_wafer_data['Data']
#     if sample_wafer_data['Unit'] is None:
#         name = list(data.keys())[0]
#         ND[name] = len(data[name]['measure'])
#         ND_Ticks[name] = (0, name)
#     else:
#         all_measure = []
#         for cnt, (key, value) in enumerate(data.items()):
#             measures = list(map(float, value['measure'].values()))
#             ND[key] = len(measures)
#             ND_Ticks[key] = (cnt, value['name'])
#             all_measure.extend(measures)
#
#         min_value, max_value = min(all_measure), max(all_measure)
#         space_value = int(max_value - min_value) + 1
#         space = 1 if space_value < 100 else space_value // 100
#
#         for i in range(space_value):
#             bin_min = space * i + min_value
#             RD[i] = sum(1 for j in all_measure if j < bin_min)
#             RD_Ticks[i] = (i, str(bin_min))
#
#     bin_data = {
#         "ND": {1: ND},
#         "RD": {1: RD},
#         "ND_Ticks": ND_Ticks,
#         "RD_Ticks": RD_Ticks,
#         "min": min_value,
#         "max": max_value,
#     }
#
#     return bin_data


def get_waferSummary(df, test_name=None, signal=None, split: int = 5, x=None, y=None):
    sample_wafer_data = {}
    name_Signal_data = {}
    names_data = {}
    xy_data = {}
    if signal:
        name_Signal_data = testNames_measure(df, test_name, signal, split)
    elif x and y:
        xy_data = testNames_measure(df, test_name, x=x, y=y, split=split)
    else:
        names_data = testNames_measure(df, test_name, split=split)
    # names_data = testNames_measure(df, precision, test_name, split=split)
    if name_Signal_data:
        sample_wafer_data.update({
            'ND': name_Signal_data['ND'],
            'RD': name_Signal_data['RD'],
            'ND_Ticks': name_Signal_data['ND_Ticks'],
            'RD_Ticks': name_Signal_data['RD_Ticks'],
            'RDmin': name_Signal_data['min'],
            'RDmax': name_Signal_data['max'],
            'RDtestName': f"{test_name} {signal}"
        })
    if xy_data:
        x = xy_data['x']
        y = xy_data['y']
        sample_wafer_data.update({
            'ND': xy_data['ND'],
            'RD': xy_data['RD'],
            'ND_Ticks': xy_data['ND_Ticks'],
            'RD_Ticks': xy_data['RD_Ticks'],
            'RDmin': xy_data['min'],
            'RDmax': xy_data['max'],
            'RDtestName': f"{test_name} {signal} X: {x} Y: {y}",
        })
    if names_data:
        sample_wafer_data.update({
            'ALLND': names_data['ND'],
            'ALLRD': names_data['RD'],
            'ALLND_Ticks': names_data['ND_Ticks'],
            'ALLRD_Ticks': names_data['RD_Ticks'],
            'ALLRDmin': names_data['min'],
            'ALLRDmax': names_data['max'],
            'ALLRDtestName': test_name
        })
    return sample_wafer_data


def convert_unit(value, input_unit, target_unit):
    unit_map = {
        "mV": {"V": 1e-3, "uV": 1e3, "nV": 1e6},
        "V": {"mV": 1e3, "uV": 1e6, "nV": 1e9},
        "uV": {"mV": 1e-3, "V": 1e-6, "nV": 1e3},
        "nV": {"mV": 1e-6, "V": 1e-9, "uV": 1e-3},
        "mA": {"A": 1e-3, "uA": 1e3, "nA": 1e6},
        "A": {"mA": 1e3, "uA": 1e6, "nA": 1e9},
        "uA": {"mA": 1e-3, "A": 1e-6, "nA": 1e3},
        "nA": {"mA": 1e-6, "A": 1e-9, "uA": 1e-3}
    }

    if input_unit == target_unit:
        return value

    try:
        conversion_factor = unit_map[input_unit][target_unit]
        return value * conversion_factor
    except KeyError:
        raise ValueError(f"Conversion from {input_unit} to {target_unit} not supported.")


def testNames_measure(df, test_name: str = None, signal: str = None, split: int = 5, x=None, y=None):
    # Extract columns containing "Measure"
    measure_cols = [col for col in df.columns if 'Measure' in col]
    if not measure_cols:
        return {}
    unit_conversion = {
        "mV": 1, "V": 1000, "uV": 0.001, "nV": 0.000001,
        "mA": 1, "A": 1000, "uA": 0.001, "nA": 0.000001,
    }
    all_measure = []
    # Filter rows by test name and signal
    if x and y:
        measure = None
        for col in measure_cols:
            XCoord = df[df['Force'] == 'XCoord']
            YCoord = df[df['Force'] == 'YCoord']
            if x == XCoord[col].values[-1] and y == YCoord[col].values[-1]:
                measure = col
        if measure:
            measure_df = df[measure]
            testNames = df['TestName']
            for name, mea in zip(testNames, measure_df):
                if name == test_name:
                    value_and_unit = extract_value_and_unit(mea)
                    if value_and_unit:
                        u = value_and_unit['unit']
                        v = value_and_unit['value']
                        converted_value = float(v) * unit_conversion.get(u, 1)
                        all_measure.append(converted_value)
    else:
        filter_condition = (df['TestName'] == test_name)
        if signal:
            filter_condition &= (df['Signal'] == signal)
        filtered_df = df[filter_condition]
        if filtered_df.empty:
            return {}
        units = set()
        asscil_5 = False
        for measure in measure_cols:
            for value in filtered_df[measure].dropna():
                value_and_unit = extract_value_and_unit(value)
                if value_and_unit:
                    unit = value_and_unit['unit']
                    converted_value = float(value_and_unit['value']) * unit_conversion.get(unit, 1)
                    all_measure.append(converted_value)
                    if unit == '%' and units == '%':
                        asscil_5 = True
                    else:
                        unit = 'mV' if 'V' in unit else 'mA'
                    units.add(unit)
                    # test.append(value_and_unit['value'])
                    if ('mV' in units and 'mA' in units) or (asscil_5 and ('mV' in units or 'mA' in units)):
                        return {}
    if not all_measure:
        return {}

    # Sort values and calculate min, max
    all_measure.sort()
    min_value, max_value = all_measure[0], all_measure[-1]
    length = len(all_measure)

    # Ensure split is at least 2
    split = max(split, 2)
    split_edges = np.linspace(min_value, max_value, split + 1)
    ND = {}
    ND_Ticks = {}
    for i in range(split):
        lower_bound, upper_bound = split_edges[i], split_edges[i + 1]
        bin_values = [x for x in all_measure if lower_bound <= x < upper_bound]
        if i == split - 1:
            bin_values += [x for x in all_measure if x == upper_bound]
        ND[f'split_{i}'] = len(bin_values)
        ND_Ticks[f'split_{i}'] = (
            i, f'[{min(bin_values):.3f}~{max(bin_values):.3f}] N:{len(bin_values)}' if bin_values else '')
    RD = {}
    RD_Ticks = {}
    split_cdf = np.linspace(0, max_value - min_value, split)
    cdf_min_values = [split_cdf[i] + min_value for i in range(0, split)]  # Precompute cdf[i] + min_value
    ndarry_measure = np.asarray(all_measure)
    wccnt = 0
    load_index = 0
    stop = False
    for sp_num in range(split - 1):
        if stop:
            break
        split_measure = ndarry_measure[load_index:]
        index = np.where((cdf_min_values[sp_num] <= split_measure) & (split_measure < cdf_min_values[sp_num + 1]))[0]
        test_measure = split_measure[index]
        test_index = len(index)

        if test_index:
            test_min, test_max = np.min(test_measure), np.max(test_measure)
            sec_cdf = np.linspace(0, test_max - test_min, test_index)
            for j in range(test_index):
                cal = test_min + sec_cdf[j]
                wcindex = len(np.where((cal >= test_measure))[0])
                if wcindex:
                    RD[wccnt] = (load_index + wcindex) / length * 100
                    if abs(cal) > 1:
                        RD_Ticks[wccnt] = (wccnt, f'{cal:.3f}')
                    else:
                        RD_Ticks[wccnt] = (wccnt, f'{cal:.6f}')
                wccnt += 1
        else:
            wccnt += 1
        load_index += test_index

    # Compile results
    bin_data = {
        "ND": {1: ND},
        "RD": {1: RD},
        "ND_Ticks": ND_Ticks,
        "RD_Ticks": RD_Ticks,
        "min": min_value,
        "max": max_value,
        'x': x,
        'y': y
    }
    return bin_data
